---
id: params
title: Screen Parameters
sidebar_label: Screen Params
---

```tsx
import { createParam } from 'solito'

const { useParam, useParams } = createParam()
```

You can read and update multiple parameters at once using `useParams()`:

```tsx
import { createParam } from 'solito'

type Params = {
  artistSlug?: string
}

const { useParam, useParams } = createParam<Params>()

export default function Page() {
  const { params, setParams } = useParams()

  const artistSlug = params.artistSlug // optional string
  const onPressUpdateSlug = () => {
    setParams({
      // params will merge with existing ones
      artistSlug: 'drake',
    })
  }
}
```

On Web, this will update the parameters in the URL, including dynamic URL segments. On native, it will use React Navigation `params`.

For more advanced use cases, such as parameters that are numbers or arrays, you may be better off using the `useParam` hook.

`useParam` is a hook that lets you read screen parameters on both Next.js and React Native. On Native, it reads React Navigation params, and on Web, it reads query params from `next/router`.

`useParam` reads both query parameters and dynamic route parameters. A Next.js dynamic route might look like `/artists/[slug].tsx`, where `slug` is a dynamic route. `useParam('slug')` works there too. On the native side, your linking config would have a URL with `/artists/:slug`.

It also lets you update the parameter, using query parameters on Web, and React state on iOS/Android.

## Video

<iframe
  width="560"
  height="315"
  src="https://www.youtube.com/embed/0lnbdRweJtA?start=1156"
  title="YouTube video player"
  frameborder="0"
  allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
  allowfullscreen
></iframe>

<br />

Learn how to use `useParam` from [Fernando Rojo](https://twitter.com/fernandotherojo), creator of Solito, in his Next.js Conf 2021 talk.

## Quick look

```tsx twoslash
import { createParam } from 'solito'
import { Text } from 'react-native'

// first, generate the hook
const { useParam } = createParam()

export const UserName = () => {
  const [username, setUsername] = useParam('username')

  return <Text>{username}</Text>
}
```

You'll use `useParam()` instead of `useRoute().params` from React Navigation, and instead of `useRouter().query` from Next.js.

## TypeScript

```tsx twoslash
import { createParam } from 'solito'
import { Text } from 'react-native'
// ---cut---

type Query = { username: string }

const { useParam } = createParam<Query>()

export const UserName = () => {
  const [username, setUsername] = useParam('username')

  return <Text>{username}</Text>
}
```

## Create custom hooks

Rather than writing `useParam` directly into your components, you should wrap them into your own hooks.

For example, create your own `useUsername` hook:

```tsx twoslash
import { createParam } from 'solito'

type Query = { username: string }

const { useParam } = createParam<Query>()

export const useUsername = () => {
  const [username, setUsername] = useParam('username')

  return {
    username,
    setUsername,
  }
}
```

And if you want to get all the parameters for a given screen, you could do this:

```tsx twoslash
import { createParam } from 'solito'

type UserScreenParams = { username: string; referredBy?: string }

const { useParam } = createParam<UserScreenParams>()

export const useUserScreenParams = () => {
  const [username] = useParam('username')
  const [referredBy] = useParam('referredBy')

  return {
    username,
    referredBy,
  }
}
```

Then, use the hook in your screen:

```tsx
export const User = () => {
  const { username, referredBy } = useUserScreenParams()

  // You can fetch the user here
  const user = useUser({ username })

  return (
    <View>
      <Text>{username}</Text>
    </View>
  )
}
```

## Compare to React Navigation Usage

Let's compare what this hook does compared to React Navigation, to help you easily migrate.

### Before, with React Navigation

```js twoslash
import { useRoute } from '@react-navigation/native'

const useUsername = () => {
  const route = useRoute()
  const username = route.params?.username
  return username
}
```

### After, with Solito

```tsx twoslash
import { createParam } from 'solito'

const { useParam } = createParam()

export const useUsername = () => {
  const [username] = useParam('username')

  return username
}
```

## Compare to Next.js Usage

### Before, with Next.js

```ts twoslash
import { useRouter } from 'next/router'

const useUsername = () => {
  const router = useRouter()
  const username = router?.query.username
  return username
}
```

### After, with Solito

```tsx twoslash
import { createParam } from 'solito'

const { useParam } = createParam()

export const useUsername = () => {
  const [username] = useParam('username')

  return username
}
```

## Motivation

A typical use-case on Web for maintaining React State is your URL's query parameters. It lets users refresh pages & share links without losing their spot in your app.

URL-as-state is especially useful on Next.js, since `next/router` will re-render your page with shallow navigation.

`useParam` lets you leverage the power of URL-as-state, while providing a fallback to React state for usage in React Native apps.

It's essentially a replacement for `useState`.

First, create the schema for your query parameters:

```ts twoslash
type Query = {
  bookingId: string
  template: 'story' | 'square'
}
```

> The values of `Query` must be primitives, such as strings/booleans/numbers, since you can't use nested fields in a URL with next router.

Next, we're going to generate our `useParam` function:

```ts twoslash
import { createParam } from 'solito'

type Query = {
  bookingId: string
  template: 'story' | 'square'
}

const { useParam } = createParam<Query>()
```

This usage of a factory is similar to `react-navigation`'s `createStackNavigator`. It allows us to have great TypeScript safety.

## Usage

Now that we've created our `useParam` function, call it in your component:

```ts twoslash
import { createParam } from 'solito'
// ---cut---

type Query = {
  bookingId: string
  template: 'story' | 'square'
}

const { useParam } = createParam<Query>()

export function App() {
  const [bookingId, setBookingId] = useParam('bookingId')
}
```

Whenever you call `setBookingId`, it will update the query parameter in the URL. To remove the query parameter, call `setBookingId(null)`.

On native, this will function as normal React State.

### Initial value

With React state, we pass an initial value like this:

```ts twoslash
import { useState } from 'react'
// ---cut---

const [selected, setSelected] = useState(true)
```

With `useParam` we achieve the same thing with the `initial` property:

```ts twoslash
import { createParam } from 'solito'

type Query = {
  bookingId: string
  template: 'story' | 'square'
}

const { useParam } = createParam<Query>()
// ---cut---

const [template, setTemplate] = useParam('template', {
  initial: 'story',
})
```

However, on web, this might not aways be the initial value. This is because the initial value itself could be set from the URL on the first navigation.

`initial` gets used on Web when these two cases are satisfied:

1. the query param (in this case, `template`) is `undefined`
2. you haven't called the set state function yet (in this case, `setTemplate`)

There is might appear to be an edge case here. What happens if you call `setTemplate(null)`? This will remove the query parameter from the URL, so we're left with an empty state. But it also won't fall back to the `initial` field, since this wouldn't match the React state behavior.

Can we find a way to provide a fallback value on Web in this case, to make sure that our URL isn't the only source of truth?

The solution lies with the `parse` field.

### Parsing values

One issue with having state in URLs is, users have an API to inject whatever state they want into your app.

This could break in many ways.

Take our `Query` type we wrote earlier:

```ts twoslash
type Query = {
  bookingId: string
  template: 'story' | 'square'
}
```

Our `template` is a **required** field that accepts `square` or `story`.

A naive approach would use it like this:

```ts twoslash
import { createParam } from 'solito'

type Query = {
  bookingId: string
  template: 'story' | 'square'
}

const { useParam } = createParam<Query>()
// ---cut---

const [template, setTemplate] = useParam('template', {
  initial: 'story',
})
```

There are two problems here: what if the URL doesn't have `template`? Or worse, what if it _does_ have `template`, but it doesn't match one of the types you specified?

Enter `parse`:

```ts twoslash
import { createParam } from 'solito'
// ---cut---

type Query = {
  bookingId: string
  template: 'story' | 'square'
}

const { useParam } = createParam<Query>()

const [template, setTemplate] = useParam('template', {
  initial: 'story',
  parse: (templateFromUrl) => {
    if (templateFromUrl === 'story' || templateFromUrl === 'square') {
      return templateFromUrl
    }
    return 'story'
  },
})
```

`parse` is the final piece of the puzzle. It lets you ensure that any state you're using from your URL is "safe".

It's also strictly typesafe, which is an added bonus.

The argument it receives will always be a string

`parse` gets run when this case is satisfied:

1. the query param (in this case, `template`) is not `undefined`

:::warning

The `parse` function will only run on Web, not on Native. To configure a `parse` function on native, you must use the `parse` option from your [React Navigation linking config](https://reactnavigation.org/docs/configuring-links/#passing-params).

:::

### Strict Types

This hook has great strict types.

The state value it returns will always be `State | undefined`, unless you pass _both_ an `initial` value and `parse`. That way, we know that on both Web and native, we're always using values which match our state.

### Stringify

It's possible you'll want to customize the way that the query param is stored in the URL.

If so, you can use the `stringify` property:

```ts
type Query = {
  bookingId: string
  template: 'story' | 'square'
}

const { useParam } = createParam<Query>()

const [bookingId, setBookingId] = useParam('bookingId', {
  stringify: (bookingId) => {
    // if we call setBookingId('123')
    // URL will be ?bookingId=artist-123
    return `artist-${bookingId}`
  },
  parse: (bookingIdFromUrl) => {
    // remember that URL params can be arrays
    if (Array.isArray(bookingIdFromUrl)) {
      return bookingIdFromUrl[0]?.replace('artist-', '')
    }
    return bookingIdFromUrl?.replace('artist-', '')
  },
  initial: '',
})
```
